/*
 * Contributor(s): Yuan An yuan.an@ishcool.drexel.edu
 */
package main.graph;

import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

import main.model.Attribute;
import main.model.Cardinality;
import main.model.Entity;
import main.model.Relationship;
import y.base.Edge;
import y.base.EdgeCursor;
import y.base.Node;
import y.base.NodeCursor;
import edu.stanford.smi.protegex.owl.ProtegeOWL;
import edu.stanford.smi.protegex.owl.model.OWLAllValuesFrom;
import edu.stanford.smi.protegex.owl.model.OWLCardinality;
import edu.stanford.smi.protegex.owl.model.OWLDatatypeProperty;
import edu.stanford.smi.protegex.owl.model.OWLMaxCardinality;
import edu.stanford.smi.protegex.owl.model.OWLMinCardinality;
import edu.stanford.smi.protegex.owl.model.OWLModel;
import edu.stanford.smi.protegex.owl.model.OWLNamedClass;
import edu.stanford.smi.protegex.owl.model.OWLObjectProperty;
import edu.stanford.smi.protegex.owl.model.OWLProperty;
import edu.stanford.smi.protegex.owl.model.OWLRestriction;
import edu.stanford.smi.protegex.owl.model.OWLSomeValuesFrom;
import edu.stanford.smi.protegex.owl.model.RDFProperty;
import edu.stanford.smi.protegex.owl.model.RDFResource;
import edu.stanford.smi.protegex.owl.model.RDFSDatatype;

/**
 * Implementation of methods to extract a graph from an OWL ontology
 * 
 * @author Yuan An
 */

public class GetCMGraph {

	/**
	 * create a valuedGraph object from the given owl onotlogy.
	 * 
	 * @param ontology
	 * @return
	 */

	public static ValuedGraph getGraph(File file) {
		ValuedGraph g = new ValuedGraph();

		// a map from classes to nodes
		HashMap<OWLNamedClass, Node> fromClsToNode = new HashMap<OWLNamedClass, Node>();
		// a map from object properties to lists of edges:
		//a property may result in multiple edges due to multiple subclasses of a range class
		HashMap<OWLProperty, ArrayList<Edge>> fromPropertyToEdges = new HashMap<OWLProperty, ArrayList<Edge>>();

		try {
			// read in the ontology file and create a OWLModel
			OWLModel owlModel = ProtegeOWL
					.createJenaOWLModelFromReader(new FileReader(file));

			// get the collection of all user defined classes
			Collection classes = owlModel.getUserDefinedOWLNamedClasses();
			Iterator it = classes.iterator();
			while (it.hasNext()) {
				OWLNamedClass cls = (OWLNamedClass) it.next();

				if (!cls.getBrowserText().equals("ReifiedRelationshipClass")) {
					Node clsNode = (Node) fromClsToNode.get(cls);
					if (clsNode == null) 
						clsNode = createNodeEntity(cls, g, fromClsToNode);

					// create edges in graph g from object properties,
					// subClassOf relationships,
					// restrictions. For subclasses, remember to update parents
					// and children of
					// an entity; for disjoint restrictions, remeber to update
					// disjoints of
					// an entity.
					createRelationshipEdges(cls, g, fromClsToNode,
							fromPropertyToEdges);

				}// for not ReifiedRelationshipClass
				else {
					// for the ReifiedRelationshipClass class, create a node
					// with
					// associated entity for each of its subclass
					createNodesFromReifiedRelationshipClasses(cls, g,
							fromClsToNode);

				}// for ReifiedRelationshipClass
			}// for each class
		}// try
		catch (Exception e) {
			e.printStackTrace();
		}

		return g;
	}

	/**
	 * create a node in the graph; create an entity from the cls, associate the node with the entity in the graph;
	 * map the cls to the node; set the entity to refer to the node.
	 * @param cls
	 * @param g
	 * @param fromClsToNode
	 */
	public static Node createNodeEntity(OWLNamedClass cls, ValuedGraph g, HashMap<OWLNamedClass, Node> fromClsToNode){
		
		Node node = g.createNode();
				
		fromClsToNode.put(cls, node);
		Entity entity = createEntityFromCls(g, node, cls);
		entity.setRefNode(node);// associate the entity with the clsNode
		g.putNodeValue(node, entity);
		
		g.putLabel2Element(entity.getName(), node);
		
		return node;
	}
	
	/**
	 * create an Entity object from an OWLClass with all attributes
	 * 
	 * @param cls
	 * @return
	 */
	public static Entity createEntityFromCls(ValuedGraph g, Node node, OWLNamedClass cls) {

		Entity entity = new Entity(cls);

		Set properties = cls.getAssociatedProperties();
		Iterator it = properties.iterator();
		while (it.hasNext()) {
			RDFProperty rdfProperty = (RDFProperty) it.next();

			if (rdfProperty instanceof OWLDatatypeProperty) {
				OWLDatatypeProperty property = (OWLDatatypeProperty) rdfProperty;
				if (property.isDomainDefined(true)) {
					// create attribute from each datatype property

					String propertyName = property.getBrowserText();
					RDFSDatatype type = property.getRangeDatatype();
					if(type == null)
						type = getTypeFromRestrictions(cls, property);
					
					Cardinality card = new Cardinality(1, -1);
					// new Cardinality(1,*);

					if (property.isFunctional())
						card.setUpper(1);

					Attribute attr = new Attribute(property, propertyName,
							type, card);
					if (property.isInverseFunctional())
						attr.setInverseFunctional();

					Collection domains = property.getDomains(false);
					if (!domains.contains(cls))
						attr.setInherited();

					// check cardi restriction of this slot.
					updateAttributeCardi(cls, attr, property);

					entity.addAttribute(attr);
					
					//create a graph node from the attribute and attach it to the entity node
					createAttributeNode(g, entity, node, attr);

				}// if the datatype property's domain is defined.
			}// for datatype property
		}// for each property

		return entity;
	}

	/**
	 * get the datatype for the datatype property from allValuesFrom or someValuesFrom 
	 * restrictions.
	 * @param cls
	 * @param property
	 * @return
	 */
	public static RDFSDatatype getTypeFromRestrictions(OWLNamedClass cls, OWLDatatypeProperty property){
		
		//get allValueFrom and someValueFrom resrictions
		Collection restrictions = cls.getRestrictions();
		Iterator it = restrictions.iterator();
		while (it.hasNext()) {
			OWLRestriction restr = (OWLRestriction) it.next();
			if (restr.getOnProperty() == property) {
				if (restr instanceof OWLAllValuesFrom) {
					RDFResource filler = ((OWLAllValuesFrom)restr).getAllValuesFrom();
					if(filler instanceof RDFSDatatype)
						return (RDFSDatatype) filler;
				}
				if (restr instanceof OWLSomeValuesFrom) {
					RDFResource filler = ((OWLSomeValuesFrom)restr).getSomeValuesFrom();
					if(filler instanceof RDFSDatatype)
						return (RDFSDatatype) filler;
				}
			}// on the given property
		}// for each restriction
		
		return null;
	}
	
	/**
	 * check restriction on the property
	 * 
	 * @param cls
	 * @param attr
	 * @param property
	 */
	public static void updateAttributeCardi(OWLNamedClass cls, Attribute attr,
			OWLDatatypeProperty property) {
		Collection restrictions = cls.getRestrictions();
		Iterator it = restrictions.iterator();
		while (it.hasNext()) {
			OWLRestriction restr = (OWLRestriction) it.next();
			if (restr.getOnProperty() == property) {
				if (restr instanceof OWLMaxCardinality) {
					attr.getCardinality().setUpper(
							((OWLMaxCardinality) restr).getCardinality());
				}
				if (restr instanceof OWLMinCardinality) {
					attr.getCardinality().setLower(
							((OWLMinCardinality) restr).getCardinality());
				}
				if (restr instanceof OWLCardinality) {
					attr.getCardinality().setLower(
							((OWLCardinality) restr).getCardinality());
					attr.getCardinality().setUpper(
							((OWLCardinality) restr).getCardinality());
				}
			}// on the given property
		}// for each restriction
	}
	
	//create a graph node from the attribute and attach it to the entity node
	public static void createAttributeNode(ValuedGraph g, Entity entity, Node node, Attribute attr){
		Node attrNode = g.createNode();
		g.putNodeValue(attrNode, attr);
		attr.setRefNode(attrNode);
		
		g.putLabel2Element(attr.getName(), attrNode);
		
		//create the edge between entity node to the attribute node
		Edge e = g.createEdge(node, attrNode);
		Cardinality cardi = new Cardinality(1,1);
		Relationship attrRel = new Relationship(entity, attr, null, e, 
				attr.getName(), Relationship.ATTRIBUTE, cardi, cardi);
		g.putEdgeValue(e, attrRel);
		
		//don't need to associate the attribute label with the edge pointing to the attribute.
		//the attribute label has already been associated with the attribute node.
	}

	/**
	 * create nodes from all subclasses of ReifiedRelationshipClass
	 * 
	 * @param cls
	 * @param g
	 * @param fromClsToNode
	 */
	public static void createNodesFromReifiedRelationshipClasses(
			OWLNamedClass cls, ValuedGraph g,
			HashMap<OWLNamedClass, Node> fromClsToNode) {
		Collection subclses = cls.getSubclasses(false);
		Iterator it = subclses.iterator();
		while (it.hasNext()) {
			Object subclsObj = it.next();
			if (subclsObj instanceof OWLNamedClass) {
				OWLNamedClass subcls = (OWLNamedClass) subclsObj;
				Node subclsNode = fromClsToNode.get(subcls);
				if (subclsNode == null)
					subclsNode = createNodeEntity(subcls, g, fromClsToNode);
					
				((Entity) g.getNodeValue(subclsNode)).setReified();
				
			}// for each OWLNamedClass subclass which is a
			// reifiedRelationshipClass
		}// for each subclass
	}

	/**
	 * create edges in the graph from object properties and subclassof
	 * relationships as well as associated restrictions
	 * 
	 * @param cls
	 * @param g
	 * @param fromClsToNode
	 * @param fromPropertyToEdge
	 */
	public static void createRelationshipEdges(OWLNamedClass cls,
			ValuedGraph g, HashMap<OWLNamedClass, Node> fromClsToNode,
			HashMap<OWLProperty, ArrayList<Edge>> fromPropertyToEdges) {

		// create edge for each subClassOf relationship -> update parents and
		// children
		createRelationshipEdgesFromSubClassOf(cls, g, fromClsToNode);

		// create edge for each object property as well as allValueFrom and someValueFrom restrictions
		createRelationshipEdgesFromObjectProperty(cls, g, fromClsToNode,
				fromPropertyToEdges);

		// update disjoint classes
		updateDisjointclasses(cls, g, fromClsToNode);
	}

	/** create edge for each subClassOf relationship -> update parents and children
	 * 
	 * @param cls
	 * @param g
	 * @param fromClsToNode
	 * @param fromPropertyToEdge
	 */
	public static void createRelationshipEdgesFromSubClassOf(OWLNamedClass cls,
			ValuedGraph g, HashMap<OWLNamedClass, Node> fromClsToNode) {

		Node node = fromClsToNode.get(cls);
		
		Collection subclses = cls.getSubclasses(false);
		Iterator subclsesIt = subclses.iterator();
		while (subclsesIt.hasNext()) {
			Object subclsObj = subclsesIt.next();
			if (subclsObj instanceof OWLNamedClass) {
				OWLNamedClass subcls = (OWLNamedClass) subclsObj;
				Node subclsNode = fromClsToNode.get(subcls);
				if (subclsNode == null) 
					subclsNode = createNodeEntity(subcls, g, fromClsToNode);

				Cardinality sourceCardi = new Cardinality(0, 1);
				Cardinality targetCardi = new Cardinality(1, 1);

				 //add subcls to this's children list and this to subcls's parent list.
				Entity entity = (Entity) g.getNodeValue(node);
				Entity subclsEntity = (Entity) g.getNodeValue(subclsNode);
				
				entity.addChild(subclsEntity);
				subclsEntity.addParent(entity);
					
				//create a new edge from subcls to cls
				Edge edge = g.createEdge(subclsNode, node);
				
				//create a new relationship corresponding to the edge
				Relationship relationship = new Relationship(subclsEntity, entity, 
						null, edge, "isa", Relationship.SPECIALIZATION, null, null);
				
				//set source and target cardinality: an isa edge is viewed as a functional edge
				//from subclass to superclass with cardinalities (0,1) - (1, 1)
				relationship.setSourceCardinality(sourceCardi);
				relationship.setTargetCardinality(targetCardi);

				g.putEdgeValue(edge, relationship);
				
				//don't associate the label "isa" with an ISA edge.

			}// for each OWLNamedClass subclass
		}// for each subclass
	}

	/**
	 * create edge for each object property
	 * 
	 * @param cls
	 * @param g
	 * @param fromClsToNode
	 * @param fromPropertyToEdge
	 */
	public static void createRelationshipEdgesFromObjectProperty(
			OWLNamedClass cls, ValuedGraph g,
			HashMap<OWLNamedClass, Node> fromClsToNode,
			HashMap<OWLProperty, ArrayList<Edge>> fromPropertyToEdges) {
		
		// create edge for each object property
		Set properties = cls.getAssociatedProperties();
		Iterator it = properties.iterator();
		while (it.hasNext()) {
			RDFProperty rdfProperty = (RDFProperty) it.next();

			if (rdfProperty instanceof OWLObjectProperty) {
				OWLObjectProperty property = (OWLObjectProperty) rdfProperty;
				if (property.isDomainDefined()) {
					//get the collection containing the classes in the range
					//as well as allValueFrom and someValueFrom restrictions
					Collection targets = getTargetClsesFromRangeRestrictions(cls, property);
					Iterator targetsIt = targets.iterator();
					while(targetsIt.hasNext()){
						RDFResource rangeResource = (RDFResource) targetsIt.next();
						if(rangeResource instanceof OWLNamedClass){
							OWLNamedClass rangeCls = (OWLNamedClass) rangeResource;
							
							//create an edge from this class as domain to the rangeCls;
							//check whether there is already edge as its inverse property;
							//if there is, don't create a new edge but update cardinality.
							createRelationshipEdgeFromDomainToRange(cls, rangeCls, g, property, fromClsToNode, fromPropertyToEdges);
							
							//create edges from this class to each subclass of the range class
							createRelationshipEdgeFromDomainToRangeSubclses(cls, rangeCls, 
									g, property, fromClsToNode, fromPropertyToEdges);
							
						}//range is a OWLNamedclass
					}//for each class in the range
				}// if the object property's domain is defined.
			}// for object property
		}// for each property
	}

	/**
	 * get the collection containing the classes in the range as well as 
	 * allValueFrom and someValueFrom restrictions.
	 * 
	 * @param cls
	 * @param property
	 * @return
	 */
	public static Collection  getTargetClsesFromRangeRestrictions(OWLNamedClass cls, OWLProperty property){
		Collection ans = new ArrayList();
		
		//get the range of the property
		Collection targets = property.getRanges(false);
		Iterator targetsIt = targets.iterator();
		while(targetsIt.hasNext()){//only consider OWLNamedClass range class
			Object obj = targetsIt.next();
			if(obj instanceof OWLNamedClass)
				if(!((OWLNamedClass)obj).getBrowserText().equals("owl:Thing"))
					ans.add(obj);
		}
		
		//get allValueFrom and someValueFrom resrictions
		Collection restrictions = cls.getRestrictions();
		Iterator it = restrictions.iterator();
		while (it.hasNext()) {
			OWLRestriction restr = (OWLRestriction) it.next();
			if (restr.getOnProperty() == property) {
				if (restr instanceof OWLAllValuesFrom) {
					RDFResource filler = ((OWLAllValuesFrom)restr).getAllValuesFrom();
					if(filler instanceof OWLNamedClass)
						if(!((OWLNamedClass)filler).getBrowserText().equals("owl:Thing"))
							ans.add(filler);
				}
				if (restr instanceof OWLSomeValuesFrom) {
					RDFResource filler = ((OWLSomeValuesFrom)restr).getSomeValuesFrom();
					if(filler instanceof OWLNamedClass)
						if(!((OWLNamedClass)filler).getBrowserText().equals("owl:Thing"))
							ans.add(filler);
				}
			}// on the given property
		}// for each restriction
		
		return ans;
	}
	
	/** create an edge from this class as domain to the rangeSubcls; 
	 * check whether there is already edge as its inverse property; 
	 * if there is, don't create a new edge but update cardinality.
	 * 
	 */
	public static void createRelationshipEdgeFromDomainToRange(OWLNamedClass domain, OWLNamedClass range, ValuedGraph g,
			OWLObjectProperty property, HashMap<OWLNamedClass, Node> fromClsToNode, 
			HashMap<OWLProperty, ArrayList<Edge>> fromPropertyToEdges){
		
		Node domainNode = fromClsToNode.get(domain);
		Node rangeNode = fromClsToNode.get(range);	
		if(rangeNode == null)
			rangeNode = createNodeEntity(range, g, fromClsToNode);
		
		//System.out.println("GetCMGraph.createRelationshipEdgeFromDomainToRange: domainNode: " + domainNode);
		//System.out.println("GetCMGraph.createRelationshipEdgeFromDomainToRange: rangeNode: " + rangeNode);
		
		OWLObjectProperty inverseProperty = (OWLObjectProperty)property.getInverseProperty();
		if(inverseProperty != null && getEdgeForPropertyBetweenNodes(fromPropertyToEdges, inverseProperty, rangeNode,
				domainNode) != null){
			//if there exists an edge for the inverse property, don't need create a new edge; but
			//update the cardinality of the inverse edge
			Edge inverseEdge = getEdgeForPropertyBetweenNodes(fromPropertyToEdges, inverseProperty, 
					rangeNode, domainNode);
			Relationship relationshipInverseEdge = (Relationship)g.getEdgeValue(inverseEdge);
			relationshipInverseEdge.addMergedProperty(property);
			relationshipInverseEdge.setRefInverseProperty(property);
			
			//update the source cardinality of the relationshipInverseEdge
			Cardinality sourceCardi = new Cardinality();
			if(property.isFunctional())
				sourceCardi.setUpper(1);
			relationshipInverseEdge.setSourceCardinality(sourceCardi);
			
			//update the source cardinality if there are cardinality restrictions
			updateRelationshipCardiFromCardiRestrictions(domain, property, relationshipInverseEdge, true);
		}
		else{//create a new edge for the property
			ArrayList<Edge> edges = fromPropertyToEdges.get(property);
			if(edges == null){
				edges = new ArrayList<Edge>();
				fromPropertyToEdges.put(property, edges);
			}
			
			//create the edge and relationship
			Edge edge = g.createEdge(domainNode, rangeNode);
			edges.add(edge);
			Entity domainEntity = (Entity)g.getNodeValue(domainNode);
			Entity rangeEntity = (Entity)g.getNodeValue(rangeNode);
			Cardinality sourceCardi = new Cardinality();
			Cardinality targetCardi = new Cardinality();
			Relationship relationship = new Relationship(domainEntity, rangeEntity, property, edge, 
					property.getBrowserText(), Relationship.ASSOCIATION, sourceCardi, targetCardi);
			
			g.putEdgeValue(edge, relationship);
			
			g.putLabel2Element(relationship.getName(), edge);
			
			//update cardinality according to other restrictions
			if(property.isFunctional())
				targetCardi.setUpper(1);
			updateRelationshipCardiFromCardiRestrictions(domain, property, relationship, false);
		}	
	}
	
	//create edges from this class to each subclass of the range class
	public static void createRelationshipEdgeFromDomainToRangeSubclses(OWLNamedClass cls, 
			OWLNamedClass rangeCls, ValuedGraph g, OWLObjectProperty property, 
			HashMap<OWLNamedClass, Node> fromClsToNode, HashMap<OWLProperty, ArrayList<Edge>> fromPropertyToEdges){
		
		Collection rangeSubclses = rangeCls.getSubclasses(true);
		Iterator rangeSubclsesIt = rangeSubclses.iterator();
		
		 
		
		    while(rangeSubclsesIt.hasNext()){
			Object rangeSubclsObj = rangeSubclsesIt.next();
			if (rangeSubclsObj instanceof OWLNamedClass) {
				OWLNamedClass rangeSubcls = (OWLNamedClass) rangeSubclsObj;
			
				//create an edge from this class as domain to the rangeSubcls;
				//check whether there is already edge as its inverse property;
				//if there is, don't create a new edge but update cardinality.
				createRelationshipEdgeFromDomainToRange(cls, rangeSubcls, g, property, fromClsToNode, fromPropertyToEdges);
			
			}//for a OWLNamedclass subclass of a range class
		}//for each subclass of a range class
	}
	
	/**
	 * get the edge corresponding to the property between the source node and the 
	 * target node
	 * @param g
	 * @param fromPropertyToEdges
	 * @param inverseProperty
	 * @param from
	 * @param to
	 * @return
	 */
	public static Edge getEdgeForPropertyBetweenNodes(HashMap<OWLProperty, ArrayList<Edge>> fromPropertyToEdges, 
			OWLObjectProperty property, Node source, Node target){
		Edge ans = null;
		
		ArrayList<Edge> edges = fromPropertyToEdges.get(property);
		
		if(edges == null)
			return null;
		
		for(Edge edge: edges){
			Node edgeSource = edge.source();
			Node edgeTarget = edge.target();
			if(edgeSource == source && edgeTarget == target){
				ans = edge;
				break;
			}
		}
		
		return ans;
	}
	
	/** update the (source, if source ==true ) cardinality if there are 
	 * cardinality restrictions of the cls on the property
	 * 
	 * @param cls
	 * @param property
	 * @param relaionship
	 * @param source
	 */
	public static void updateRelationshipCardiFromCardiRestrictions(OWLNamedClass cls, 
			OWLProperty property, Relationship relationship, boolean source){
		
		Collection restrictions = cls.getRestrictions();
		Iterator it = restrictions.iterator();
		
		while (it.hasNext()) {
			OWLRestriction restr = (OWLRestriction) it.next();
			if (restr.getOnProperty() == property) {
				if (restr instanceof OWLMaxCardinality) {
					int cardi = ((OWLMaxCardinality)restr).getCardinality();
					if(source)
						relationship.getSourceCardinality().setUpper(cardi);
					else
						relationship.getTargetCardinality().setUpper(cardi);
					
				}
				if (restr instanceof OWLMinCardinality) {
					int cardi = ((OWLMinCardinality)restr).getCardinality();
					if(source)
						relationship.getSourceCardinality().setLower(cardi);
					else
						relationship.getTargetCardinality().setLower(cardi);
				}
				if (restr instanceof OWLCardinality) {
					int cardi = ((OWLCardinality)restr).getCardinality();
					if(source){
						relationship.getSourceCardinality().setLower(cardi);
						relationship.getSourceCardinality().setUpper(cardi);
					}
					else{
						relationship.getTargetCardinality().setLower(cardi);
						relationship.getTargetCardinality().setUpper(cardi);
					}
				}
			}// on the given property
		}// for each restriction
	}
	
	/** update disjoint classes
	 * 
	 * @param cls
	 * @param g
	 * @param fromClsToNode
	 * @param fromPropertyToEdge
	 */
	public static void updateDisjointclasses(OWLNamedClass cls, ValuedGraph g,
			HashMap<OWLNamedClass, Node> fromClsToNode) {
		
		Node clsNode = fromClsToNode.get(cls);
		Entity clsEntity = (Entity)g.getNodeValue(clsNode);
		
		Collection disjoints = cls.getDisjointClasses();
		Iterator it = disjoints.iterator();
		while(it.hasNext()){
			OWLNamedClass disjoint = (OWLNamedClass) it.next();
			Node disjointNode = fromClsToNode.get(disjoint);
			
			if(disjointNode == null)
				disjointNode = createNodeEntity(disjoint, g, fromClsToNode);
			
			Entity disjointEntity = (Entity)g.getNodeValue(disjointNode);
			clsEntity.addDisjoint(disjointEntity);
		}
	}
	
	public static String displayCMGraph(ValuedGraph cmg){
		String ans = "";
		
		
		//print each node
		ans += "========The total number of nodes is: " + cmg.N() + "==========\n";
		for(NodeCursor nc = cmg.nodes(); nc.ok(); nc.next()){
			Node n = nc.node();
			Object v = cmg.getNodeValue(n);
			if(v instanceof Entity){
				Entity entity = (Entity) v;
				ans += entity.display() + "\n";
			}
			else if (v instanceof Attribute){
				Attribute attr = (Attribute) v;
				ans += attr.display() + "\n";
			}
		}
		
		
		//print each edge
		ans += "========The total number of edges is: " + cmg.E() + "==========\n";
		for(EdgeCursor ec = cmg.edges(); ec.ok(); ec.next()){
			Edge e = ec.edge();
			Relationship relationship = (Relationship) cmg.getEdgeValue(e);
			if(relationship.getType() == Relationship.ATTRIBUTE){
				Attribute  attr = (Attribute) relationship.getTarget();
				ans += attr.display() + "\n";
			}
			else {
				ans += "[" + relationship.getSource().getName() + "]";
				ans += "(" + relationship.getSourceCardinality().getLower() + "," + relationship.getSourceCardinality().getUpper() + ")";
				ans += "-----<" + relationship.getName();
				if(relationship.getRefInverseProperty() != null)
					ans += "/" + relationship.getRefInverseProperty().getBrowserText();
				ans += ">-----";
				ans += "(" + relationship.getTargetCardinality().getLower() + "," + relationship.getTargetCardinality().getUpper() + ")";
				ans += "[" + relationship.getTarget().getName() + "]\n";
			}
		}
		
		
		return ans;
	}
	
			
}